/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package algorithm;

import graph.ConditionsProperties;
import graph.DijkstraResult;
import graph.Edge;
import graph.Graph;
import graph.Node;
import java.util.AbstractQueue;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 *
 * @author borysfan
 */
public class Dijkstra {
    private Graph g;

    public void prepareToDijkstry(){
        Graph g = Graph.getInstance();
        ArrayList<Node> nodeList = g.getNodes();
        for(int i=0;i<nodeList.size();i++){
            Node n = nodeList.get(i);
            n.setDistance(Double.MAX_VALUE);
            n.setOnPath(false);
            n.setPrev(null);
            n.clearPrev();
        }
    }

    public void compute(Node startNode){
        PriorityQueue<Node> pq = new PriorityQueue<Node>();
        prepareToDijkstry();
        startNode.setOnPath(true);
        startNode.setDistance(0);
        startNode.setPrev(null);
        for(int i=0;i<startNode.getEdges().size();i++){
            pq.add(startNode.getEdges().get(i).getEnd());
        }

        while(pq.isEmpty()==false){
            Node top = pq.poll();
            if(top.isLock()==true) continue;
            System.out.println("przetwarzam "+top.getName()+" "+top.getEdges().size());
            for(int i=0;i<top.getEdges().size();i++){
                Edge e = top.getEdges().get(i);
                if(e.isLock()==true) continue;
                Node tmp = e.getEnd();
                if(top==tmp) tmp = e.getStart();
                System.out.println("koniec" + tmp.getName()+" warunek: "+tmp.getDistance()+" > "+top.getDistance()+" + "+e.getLength());
                if(tmp.getDistance() > top.getDistance()+e.getLength()){
                    pq.remove(tmp);
                    tmp.setDistance(top.getDistance() + e.getLength());
                    tmp.setPrev(top);
                    pq.add(tmp);
                }
            }
        }
    }

     public void computeByTime(Node startNode,boolean weatherCondition, boolean roadCondition,boolean traffic){
        PriorityQueue<Node> pq = new PriorityQueue<Node>();

        prepareToDijkstry();
        startNode.setOnPath(true);
        startNode.setDistance(0);
        startNode.setAvgTime(0);
        startNode.setPrev(null);
        for(int i=0;i<startNode.getEdges().size();i++){
            pq.add(startNode.getEdges().get(i).getEnd());
        }

        while(pq.isEmpty()==false){
            Node top = pq.poll();
            if(top.isLock()==true) continue;
            System.out.println("przetwarzam "+top.getName()+" "+top.getEdges().size());
            for(int i=0;i<top.getEdges().size();i++){
                Edge e = top.getEdges().get(i);
                if(e.isLock()==true) continue;
                Node tmp = e.getEnd();
                if(top.equals(tmp)) tmp = e.getStart();
                if(tmp.isLock()) continue;
                double speed = e.getVMax();

                if(roadCondition){
                    System.out.println("road condition");
                    int selectedRoad = e.getRoadConditions();
                    int roadValue = e.getRoadSlider();
                    double roadPercent = ((double)roadValue)/100;
                     switch(selectedRoad){
                        case 1: speed-= speed*(ConditionsProperties.REMONT*roadPercent/100);
                                break;
                        case 2: speed-= speed*(ConditionsProperties.WYPADEK*roadPercent/100);
                                break;
                        case 3: speed-= speed*(ConditionsProperties.UTRUDNIENIA_W_RUCHU*roadPercent/100);
                                break;
                    }
                }

                if(weatherCondition){
                    System.out.println("weatherCondition");
                    int selectedWeather = e.getWeatherConditions();
                    int weatherValue = e.getWeatherSlider();
                    double weatherPercent = ((double)weatherValue)/100;
                     switch(selectedWeather){
                        case 1: speed -= speed*(ConditionsProperties.DESZCZ*weatherPercent/100);
                                break;
                        case 2: speed -= speed*(ConditionsProperties.SNIEG*weatherPercent/100);
                                break;
                        case 3: speed -= speed*(ConditionsProperties.GRAD*weatherPercent/100);
                                break;
                        case 4: speed -= speed*(ConditionsProperties.GOLOLEDZ*weatherPercent/100);
                                break;
                        case 5: speed -= speed*(ConditionsProperties.MGLA*weatherPercent/100);
                                break;
                    }
                }

                if(traffic){
                    System.out.println("traffic condition");
                    double trafficPercent = 0;
                    switch(e.getTraffic()){
                        case 0: trafficPercent = ConditionsProperties.SMALL/100;
                                break;
                        case 1: trafficPercent = ConditionsProperties.MIDDLE/100;
                                break;
                        case 2: trafficPercent = ConditionsProperties.BIG/100;
                                break;
                    }
                    speed=speed - speed*trafficPercent;
                }
                //System.out.println("koniec" + tmp.getName()+" warunek: "+tmp.getDistance()+" > "+top.getDistance()+" + "+e.getLength());
                if(tmp.getAvgTime() > top.getAvgTime()+e.getLength()/speed){
                    pq.remove(tmp);
                    tmp.setAvgTime(top.getAvgTime() + e.getLength()/speed);
                    tmp.setDistance(top.getDistance()+e.getLength());
                    tmp.setPrev(top);
                    pq.add(tmp);
                }
            }
        }
    }

    public void computeByDistance(Node startNode,boolean weatherCondition, boolean roadCondition,boolean traffic){
        PriorityQueue<Node> pq = new PriorityQueue<Node>();
        prepareToDijkstry();
        startNode.setOnPath(true);
        startNode.setDistance(0);
        startNode.setPrev(null);
        Graph g = Graph.getInstance();
        for(int i=0;i<g.getNodes().size();i++){
            g.getNodes().get(i).setAvgTime(0);
        }
        for(int i=0;i<startNode.getEdges().size();i++){
            pq.add(startNode.getEdges().get(i).getEnd());
        }

        while(pq.isEmpty()==false){
            Node top = pq.poll();
            if(top.isLock()==true) continue;
            System.out.println("przetwarzam "+top.getName()+" "+top.getEdges().size());
            for(int i=0;i<top.getEdges().size();i++){
                Edge e = top.getEdges().get(i);
                if(e.isLock()==true) continue; // jezeli krawedz jest zablokowana pomijam ją
                Node tmp = e.getEnd();
                if(top.equals(tmp)) tmp = e.getStart();
                if(tmp.isLock()==true) continue; // jezeli wierzcholek do ktorego chce dojsc jest zablokowany pomijam go
                System.out.println("koniec" + tmp.getName()+" warunek: "+tmp.getDistance()+" > "+top.getDistance()+" + "+e.getLength());

                if(tmp.getDistance() > top.getDistance()+e.getLength()){
                    pq.remove(tmp);
                    tmp.setDistance(top.getDistance() + e.getLength());
                    double speed = e.getVMax();
                    
                    if(roadCondition){
                        System.out.println("road condition");
                        int selectedRoad = e.getRoadConditions();
                        int roadValue = e.getRoadSlider();
                        double roadPercent = ((double)roadValue)/100;
                         switch(selectedRoad){
                            case 1: speed-= speed*(ConditionsProperties.REMONT*roadPercent/100);
                                    break;
                            case 2: speed-= speed*(ConditionsProperties.WYPADEK*roadPercent/100);
                                    break;
                            case 3: speed-= speed*(ConditionsProperties.UTRUDNIENIA_W_RUCHU*roadPercent/100);
                                    break;
                        }
                    }

                    if(weatherCondition){
                        System.out.println("weatherCondition");
                        int selectedWeather = e.getWeatherConditions();
                        int weatherValue = e.getWeatherSlider();
                        double weatherPercent = ((double)weatherValue)/100;
                        System.out.println("pogoda "+ weatherPercent);
                         switch(selectedWeather){
                            case 1: speed -= speed*(ConditionsProperties.DESZCZ*weatherPercent/100);
                                    break;
                            case 2: speed -= speed*(ConditionsProperties.SNIEG*weatherPercent/100);
                                    break;
                            case 3: speed -= speed*(ConditionsProperties.GRAD*weatherPercent/100);
                                    break;
                            case 4: speed -= speed*(ConditionsProperties.GOLOLEDZ*weatherPercent/100);
                                    break;
                            case 5: speed -= speed*(ConditionsProperties.MGLA*weatherPercent/100);
                                    break;
                        }
                    }

                    if(traffic){
                        System.out.println("traffic condition");
                        double trafficPercent = 0;
                        switch(e.getTraffic()){
                            case 0: trafficPercent = ConditionsProperties.SMALL/100;
                                    break;
                            case 1: trafficPercent = ConditionsProperties.MIDDLE/100;
                                    break;
                            case 2: trafficPercent = ConditionsProperties.BIG/100;
                                    break;
                        }
                        speed=speed - speed*trafficPercent;
                    }
                    tmp.setAvgTime(top.getAvgTime() + e.getLength()/speed);
                    tmp.setAvgSpeed(speed);
                    tmp.setPrev(top);
                    pq.add(tmp);
                }
            }
        }
    }

       public void computeBySpeed(Node startNode,boolean weatherCondition, boolean roadCondition,boolean traffic){
        PriorityQueue<Node> pq = new PriorityQueue<Node>();
        prepareToDijkstry();
        startNode.setOnPath(true);
        startNode.setDistance(0);
        startNode.setPrev(null);
        for(int i=0;i<startNode.getEdges().size();i++){
            pq.add(startNode.getEdges().get(i).getEnd());
        }

        while(pq.isEmpty()==false){
            Node top = pq.poll();
            if(top.isLock()==true) continue;
            System.out.println("przetwarzam "+top.getName()+" "+top.getEdges().size());
            for(int i=0;i<top.getEdges().size();i++){
                Edge e = top.getEdges().get(i);
                if(e.isLock()==true) continue;
                Node tmp = e.getEnd();
                if(top.equals(tmp)) tmp = e.getStart();
                System.out.println("koniec" + tmp.getName()+" warunek: "+tmp.getAvgSpeed()+" < "+top.getAvgSpeed()+" + "+e.getVMax());
                if(e.isVisited()==false){
                    if(tmp.getAvgSpeed() < top.getAvgSpeed()+e.getVMax()){
                        pq.remove(tmp);
                        tmp.setAvgSpeed(top.getAvgSpeed() + e.getVMax());
                        tmp.setPrev(top);
                        System.out.println("Znaleziono: "+top.getName());
                        pq.add(tmp);
                    }
                    e.setVisited(true);
                }
            }
        }
    }

    public DijkstraResult getPath(Node start, Node tar){
        ArrayList<Node> list=new ArrayList<Node>();
        System.out.println(tar.getName());
        list.add(tar);
        boolean flag = false;
        while(tar.getPrev()!=null){
            tar = tar.getPrev();
            if(tar.equals(start)) flag=true;
            list.add(tar);
            System.out.println(tar.getName());
        }
        DijkstraResult result = new DijkstraResult();
        for(int i=list.size()-1;i>=0;i--){
            result.addNode(list.get(i));
        }

        if(flag)
            return result;
        else return null;
         
    }

    public ArrayList<DijkstraResult> getAllPaths(Node start,Node tar,boolean weatherCondition, boolean roadCondition,boolean traffic){
        ArrayList<DijkstraResult> results = new ArrayList<DijkstraResult>();
        computeByDistance(start, weatherCondition, roadCondition, traffic);
        DijkstraResult partRes = getPath(start,tar);
        if(partRes!=null){
            results.add(partRes);
            Graph graph = Graph.getInstance();
            ArrayList<Node> nodesList = graph.getNodes();
            for(int i=0;i<nodesList.size();i++){
                Node a = nodesList.get(i);
                for(int j=0;j<a.getEdges().size();j++){
                    Edge e = a.getEdges().get(j);
                    if(e!=null){
                        if(e.isLock()==false){
                        e.lock();
                        prepareToDijkstry();
                        computeByDistance(start, weatherCondition, roadCondition, traffic);
                        DijkstraResult tmpRes = getPath(start,tar);
                        if(tmpRes!=null){
                            if(tmpRes.getCityNum()>0){
                                if(tmpRes.get(0).equals(start)){
                                    boolean flag = true;
                                    for(int k=0;k<results.size();k++){
                                          if(tmpRes.equals(results.get(k))==true)
                                              flag=false;
                                    }
                                    if(flag==true){
                                        results.add(tmpRes);
                                        System.out.println("Droga: " + tmpRes.getDistance());
                                    }
                                }
                            }
                             for(int k=0;k<tmpRes.getCityNum()-1;k++){
                                Node n1 = tmpRes.get(k);
                                Node n2 = tmpRes.get(k+1);
                                Edge edge = graph.getEdge(n1, n2);
                                if(edge!=null){
                                    if(edge.isLock()==false){
                                        edge.lock();
                                        prepareToDijkstry();
                                        computeByDistance(start, weatherCondition, roadCondition, traffic);
                                        DijkstraResult tmpRes2 = getPath(start,tar);
                                        if(tmpRes2!=null){
                                            if(tmpRes2.getCityNum()>0){
                                                if(tmpRes2.get(0).equals(start)){
                                                    boolean flag = true;
                                                    for(int l=0;l<results.size();l++){
                                                          if(tmpRes2.equals(results.get(l))==true)
                                                              flag=false;
                                                    }
                                                    if(flag==true){
                                                        results.add(tmpRes2);
                                                        System.out.println("Droga: " + tmpRes2.getDistance());
                                                    }
                                                }
                                            }
                                        }
                                        edge.unlock();
                                    }
                                }
                            }
                        }
                       

                        e.unlock();
                    }
                    }
                }
            }
        }
        System.out.println(results.size());
        return results;
    }

     public ArrayList<DijkstraResult> getAllPathsBySpeed(Node start,Node tar,boolean weatherCondition, boolean roadCondition,boolean traffic){
        ArrayList<DijkstraResult> results = new ArrayList<DijkstraResult>();
        computeBySpeed(start, weatherCondition, roadCondition, traffic);
        DijkstraResult partRes = getPath(start,tar);
        if(partRes!=null){
            results.add(partRes);
            Graph graph = Graph.getInstance();
            for(int i=1;i<partRes.getCityNum();i++){
                Node a = partRes.get(i-1);
                Node b = partRes.get(i);
                Edge e = graph.getEdge(a, b);
                e.lock();
                prepareToDijkstry();
                computeBySpeed(start, weatherCondition, roadCondition, traffic);
                DijkstraResult tmpRes = getPath(start,tar);
                if(tmpRes!=null){
                    if(tmpRes.getCityNum()>0){
                        if(tmpRes.get(0).equals(start)){
                            boolean flag = true;
                          /*  for(int j=0;j<results.size();j++){
                                  if(tmpRes.equals(results.get(j))==false)
                                      flag=false;
                            }*/
                            if(flag==true){
                                results.add(tmpRes);
                                System.out.println("Droga: " + tmpRes.getDistance());
                            }
                        }
                    }
                }
                e.unlock();
            }
        }
        System.out.println(results.size());
        return results;
    }

      public ArrayList<DijkstraResult> getAllPathsByTime(Node start,Node tar,boolean weatherCondition, boolean roadCondition,boolean traffic){
        ArrayList<DijkstraResult> results = new ArrayList<DijkstraResult>();
        computeByTime(start, weatherCondition, roadCondition, traffic);
        DijkstraResult partRes = getPath(start,tar);
        if(partRes!=null){
            results.add(partRes);
            Graph graph = Graph.getInstance();
            ArrayList<Node> nodesList = graph.getNodes();
            for(int i=0;i<nodesList.size();i++){
                Node a = nodesList.get(i);
                for(int j=0;j<a.getEdges().size();j++){
                    Edge e = a.getEdges().get(j);
                    if(e!=null){
                        if(e.isLock()==false){
                            e.lock();
                            prepareToDijkstry();
                            computeByTime(start, weatherCondition, roadCondition, traffic);
                            DijkstraResult tmpRes = getPath(start,tar);
                            if(tmpRes!=null){
                                if(tmpRes.getCityNum()>0){
                                    if(tmpRes.get(0).equals(start)){
                                        boolean flag = true;
                                        for(int k=0;k<results.size();k++){
                                              if(tmpRes.equals(results.get(k))==true)
                                                  flag=false;
                                        }
                                        if(flag==true){
                                            results.add(tmpRes);
                                            System.out.println("Droga: " + tmpRes.getDistance());
                                        }
                                    }
                                }
                                for(int k=0;k<tmpRes.getCityNum()-1;k++){
                                    Node n1 = tmpRes.get(k);
                                    Node n2 = tmpRes.get(k+1);
                                    Edge edge = graph.getEdge(n1, n2);
                                    if(edge!=null){
                                        if(edge.isLock()==false){
                                            edge.lock();
                                            prepareToDijkstry();
                                            computeByTime(start, weatherCondition, roadCondition, traffic);
                                            DijkstraResult tmpRes2 = getPath(start,tar);
                                            if(tmpRes2!=null){
                                                if(tmpRes2.getCityNum()>0){
                                                    if(tmpRes2.get(0).equals(start)){
                                                        boolean flag = true;
                                                        for(int l=0;l<results.size();l++){
                                                              if(tmpRes2.equals(results.get(l))==true)
                                                                  flag=false;
                                                        }
                                                        if(flag==true){
                                                            results.add(tmpRes2);
                                                            System.out.println("Droga: " + tmpRes2.getDistance());
                                                        }
                                                    }
                                                }
                                            }
                                            edge.unlock();
                                        }
                                    }
                                }
                            }

                            e.unlock();
                        }
                    }
                }
            }
        }
        System.out.println(results.size());
        return results;
    }

    public void bfs(Node start){
        prepareToDijkstry();
        Queue<Node> q = new LinkedList<Node>();
        q.add(start);
        while(q.isEmpty()==false){
            Node top = q.poll();

        }
    }

      

     
}
